﻿using System;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Input;

namespace конфигуратор
{
    /// <summary>
    /// An <see cref="ICommand"/> whose delegates can be attached for <see cref="Execute"/> and <see cref="CanExecute"/>.
    /// </summary>
    /// <typeparam name="T">Parameter type.</typeparam>
    public class DelegateCommand<T> : ICommand
    {
        private readonly Action<T> _executeMethod;
        private readonly Func<T, bool> _canExecuteMethod;
        private readonly Dispatcher _dispatcher;

        /// <summary>
        /// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
        /// </summary>
        /// <param name="executeMethod">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
        /// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
        public DelegateCommand ( Action<T> executeMethod )
            : this( executeMethod, null )
        {
        }

        /// <summary>
        /// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
        /// </summary>
        /// <param name="executeMethod">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
        /// <param name="canExecuteMethod">Delegate to execute when CanExecute is called on the command.  This can be null.</param>
        /// <exception cref="ArgumentNullException">When both <paramref name="executeMethod"/> and <paramref name="canExecuteMethod"/> ar <see langword="null" />.</exception>
        public DelegateCommand ( Action<T> executeMethod, Func<T, bool> canExecuteMethod )
        {
            if ( executeMethod == null && canExecuteMethod == null )
                throw new ArgumentNullException( "executeMethod" );

            this._executeMethod = executeMethod;
            this._canExecuteMethod = canExecuteMethod;
            if ( Application.Current != null )
            {
                _dispatcher = Application.Current.Dispatcher;
            }
        }

        ///<summary>
        ///Defines the method that determines whether the command can execute in its current state.
        ///</summary>
        ///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
        ///<returns>
        ///<see langword="true" /> if this command can be executed; otherwise, <see langword="false" />.
        ///</returns>
        public bool CanExecute ( T parameter )
        {
            if ( _canExecuteMethod == null ) return true;
            return _canExecuteMethod( parameter );
        }

        ///<summary>
        ///Defines the method to be called when the command is invoked.
        ///</summary>
        ///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
        public void Execute ( T parameter )
        {
            if ( _executeMethod == null ) return;
            _executeMethod( parameter );
        }

        ///<summary>
        ///Defines the method that determines whether the command can execute in its current state.
        ///</summary>
        ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        ///<returns>
        ///true if this command can be executed; otherwise, false.
        ///</returns>
        bool ICommand.CanExecute ( object parameter )
        {
            return CanExecute( ( T ) parameter );
        }

        ///<summary>
        ///Occurs when changes occur that affect whether or not the command should execute.
        ///</summary>
        public event EventHandler CanExecuteChanged;

        ///<summary>
        ///Defines the method to be called when the command is invoked.
        ///</summary>
        ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        void ICommand.Execute ( object parameter )
        {
            Execute( ( T ) parameter );
        }

        /// <summary>
        /// Raises <see cref="CanExecuteChanged"/> on the UI thread so every command invoker
        /// can requery to check if the command can execute.
        /// <remarks>Note that this will trigger the execution of <see cref="CanExecute"/> once for each invoker.</remarks>
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1030:UseEventsWhereAppropriate" )]
        public void RaiseCanExecuteChanged ()
        {
            OnCanExecuteChanged();
        }

        /// <summary>
        /// Raises <see cref="CanExecuteChanged"/> on the UI thread so every command invoker can requery to check if the command can execute.
        /// </summary>
        protected virtual void OnCanExecuteChanged ()
        {
            EventHandler canExecuteChangedHandler = CanExecuteChanged;
            if ( canExecuteChangedHandler != null )
            {
                if ( _dispatcher != null && !_dispatcher.CheckAccess() )
                {
                    _dispatcher.BeginInvoke( DispatcherPriority.Normal,
                                           ( Action ) OnCanExecuteChanged );
                }
                else
                {
                    canExecuteChangedHandler( this, EventArgs.Empty );
                }
            }
        }

       
    }
}
